﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using IssueVision.Common;
using IssueVision.EntityModel;
using IssueVision.WCFService.IssueVisionService;

namespace IssueVision.Model
{
    [Export(typeof(IIssueVisionModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class IssueVisionModel : IIssueVisionModel
    {
        #region "Private Data"
        private IssueVisionServiceClient _proxy;
        Boolean _updateIssueDone = true;
        Boolean _updateUserDone = true;
        private Exception _lastError;
        #endregion "Private Data"

        #region "Constructor"
        public IssueVisionModel()
        {
            _proxy = IssueVisionServiceClient.Instance;
            // Set up event handling
            _proxy.PropertyChanged += _proxy_PropertyChanged;
            _proxy.GetIssueTypesCompleted += _proxy_GetIssueTypesCompleted;
            _proxy.GetPlatformsCompleted += _proxy_GetPlatformsCompleted;
            _proxy.GetResolutionsCompleted += _proxy_GetResolutionsCompleted;
            _proxy.GetSecurityQuestionsCompleted += _proxy_GetSecurityQuestionsCompleted;
            _proxy.GetStatusesCompleted += _proxy_GetStatusesCompleted;
            _proxy.GetSubStatusesCompleted += _proxy_GetSubStatusesCompleted;
            _proxy.GetUsersCompleted += _proxy_GetUsersCompleted;
            _proxy.GetAllIssuesCompleted += _proxy_GetAllIssuesCompleted;
            _proxy.GetMyIssuesCompleted += _proxy_GetMyIssuesCompleted;
            _proxy.GetCurrentUserCompleted += _proxy_GetCurrentUserCompleted;
            _proxy.GetCurrentUserProfileResetCompleted += _proxy_GetCurrentUserProfileResetCompleted;
            _proxy.GetAllUnResolvedIssuesCompleted += _proxy_GetAllUnResolvedIssuesCompleted;
            _proxy.GetActiveBugCountByMonthCompleted += _proxy_GetActiveBugCountByMonthCompleted;
            _proxy.GetResolvedBugCountByMonthCompleted += _proxy_GetResolvedBugCountByMonthCompleted;
            _proxy.GetActiveBugCountByPriorityCompleted += _proxy_GetActiveBugCountByPriorityCompleted;
            _proxy.GetIssueHistoryByIssueIDCompleted += _proxy_GetIssueHistoryByIssueIDCompleted;
            _proxy.UpdateIssueCompleted += _proxy_UpdateIssueCompleted;
            _proxy.UpdateUserCompleted += _proxy_UpdateUserCompleted;
        }
        #endregion "Constructor"

        #region "IIssueVisionModel Interface implementation"

        public void GetIssueTypesAsync()
        {
            _proxy.GetIssueTypesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetPlatformsAsync()
        {
            _proxy.GetPlatformsAsync();
            _proxy.IncrementCallCount();
        }

        public void GetResolutionsAsync()
        {
            _proxy.GetResolutionsAsync();
            _proxy.IncrementCallCount();
        }

        public void GetSecurityQuestionsAsync()
        {
            _proxy.GetSecurityQuestionsAsync();
            _proxy.IncrementCallCount();
        }
 
        public void GetStatusesAsync()
        {
            _proxy.GetStatusesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetSubStatusesAsync()
        {
            _proxy.GetSubStatusesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetUsersAsync()
        {
            _proxy.GetUsersAsync();
            _proxy.IncrementCallCount();
        }

        public void GetAllIssuesAsync()
        {
            _proxy.GetAllIssuesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetMyIssuesAsync()
        {
            _proxy.GetMyIssuesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetCurrentUserAsync()
        {
            _proxy.GetCurrentUserAsync();
            _proxy.IncrementCallCount();
        }

        public void GetCurrentUserProfileResetAsync()
        {
            _proxy.GetCurrentUserProfileResetAsync();
            _proxy.IncrementCallCount();
        }

        public void GetAllUnresolvedIssuesAsync()
        {
            _proxy.GetAllUnResolvedIssuesAsync();
            _proxy.IncrementCallCount();
        }

        public void GetActiveBugCountByMonthAsync(int numberOfMonth)
        {
            _proxy.GetActiveBugCountByMonthAsync(numberOfMonth);
            _proxy.IncrementCallCount();
        }


        public void GetResolvedBugCountByMonthAsync(int numberOfMonth)
        {
            _proxy.GetResolvedBugCountByMonthAsync(numberOfMonth);
            _proxy.IncrementCallCount();
        }


        public void GetActiveBugCountByPriorityAsync()
        {
            _proxy.GetActiveBugCountByPriorityAsync();
            _proxy.IncrementCallCount();
        }

        public void GetIssueHistoryByIssueIDAsync(long issueID)
        {
            _proxy.GetIssueHistoryByIssueIDAsync(issueID);
            _proxy.IncrementCallCount();
        }

        public Exception GetLastError()
        {
            return _lastError;
        }

        public void ClearLastError()
        {
            _lastError = null;
        }

        public bool AllowMultipleErrors { get; set; }

        /// <summary>
        /// Keeps a reference to the current Issue
        /// item in edit
        /// </summary>
        public Issue CurrentEditIssue
        {
            get { return _currentEditIssue; }
            set
            {
                if (!CurrentEditIssueHasChanges())
                {
                    if (!ReferenceEquals(_currentEditIssue, value))
                    {
                        if (_currentEditIssue != null)
                        {
                            ((INotifyPropertyChanged)_currentEditIssue).PropertyChanged -= IssueVisionModel_PropertyChanged;
                            if (_currentEditIssue.Attributes != null)
                            {
                                _currentEditIssue.Attributes.CollectionChanged -= Attributes_CollectionChanged;
                                foreach (EntityModel.Attribute item in _currentEditIssue.Attributes)
                                    ((INotifyPropertyChanged)item).PropertyChanged -= IssueVisionModel_PropertyChanged;
                            }
                            if (_currentEditIssue.Files != null)
                            {
                                _currentEditIssue.Files.CollectionChanged -= Files_CollectionChanged;
                                foreach (File item in _currentEditIssue.Files)
                                    ((INotifyPropertyChanged)item).PropertyChanged -= IssueVisionModel_PropertyChanged;
                            }
                        }
                        _currentEditIssue = value;
                        if (_currentEditIssue != null)
                        {
                            ((INotifyPropertyChanged)_currentEditIssue).PropertyChanged += IssueVisionModel_PropertyChanged;
                            if (_currentEditIssue.Attributes != null)
                            {
                                _currentEditIssue.Attributes.CollectionChanged += Attributes_CollectionChanged;
                                foreach (EntityModel.Attribute item in _currentEditIssue.Attributes)
                                    ((INotifyPropertyChanged)item).PropertyChanged += IssueVisionModel_PropertyChanged;
                            }
                            if (_currentEditIssue.Files != null)
                            {
                                _currentEditIssue.Files.CollectionChanged += Files_CollectionChanged;
                                foreach (File item in _currentEditIssue.Files)
                                    ((INotifyPropertyChanged)item).PropertyChanged += IssueVisionModel_PropertyChanged;
                            }
                        }
                        ReCalculateHasChanges();
                    }
                }
                else
                    throw new InvalidOperationException(CommonResources.HasChangesIsTrue);
            }
        }
        private Issue _currentEditIssue;

        /// <summary>
        /// Keeps a reference to the current User
        /// item in edit
        /// </summary>
        public User CurrentEditUser
        {
            get { return _currentEditUser; }
            set
            {
                if (!CurrentEditUserHasChanges())
                {
                    if (!ReferenceEquals(_currentEditUser, value))
                    {
                        if (_currentEditUser != null)
                        {
                            ((INotifyPropertyChanged)_currentEditUser).PropertyChanged -= IssueVisionModel_PropertyChanged;
                        }
                        _currentEditUser = value;
                        if (_currentEditUser != null)
                        {
                            ((INotifyPropertyChanged)_currentEditUser).PropertyChanged += IssueVisionModel_PropertyChanged;
                        }
                        ReCalculateHasChanges();
                    }
                }
                else
                    throw new InvalidOperationException(CommonResources.HasChangesIsTrue);
            }
        }
        private User _currentEditUser;

        /// <summary>
        /// Save changes on both
        /// CurrentEditIssue and CurrentEditUser
        /// </summary>
        public void SaveChangesAsync()
        {
            if (HasChanges)
            {
                if (_currentEditIssue != null && CurrentEditIssueHasChanges())
                {
                    _proxy.UpdateIssueAsync(_currentEditIssue);
                    _proxy.IncrementCallCount();
                    _updateIssueDone = false;
                }
                if (_currentEditUser != null && CurrentEditUserHasChanges())
                {
                    _proxy.UpdateUserAsync(_currentEditUser);
                    _proxy.IncrementCallCount();
                    _updateUserDone = false;
                }
            }
        }

        /// <summary>
        /// Call RejectChanges on both
        /// CurrentEditIssue and CurrentEditUser
        /// </summary>
        public void RejectChanges()
        {
            if (_currentEditIssue != null)
            {
                _currentEditIssue.RejectChanges();
                if (_currentEditIssue.Attributes != null)
                {
                    foreach (EntityModel.Attribute item in _currentEditIssue.Attributes)
                        item.RejectChanges();
                }
                if (_currentEditIssue.Files != null)
                {
                    foreach (File item in _currentEditIssue.Files)
                        item.RejectChanges();
                }
            }
            if (_currentEditUser != null)
            {
                _currentEditUser.RejectChanges();
            }
        }

        /// <summary>
        /// True if either CurrentEditIssue or
        /// CurrentEditUser has changes; otherwise, false
        /// </summary>
        public bool HasChanges
        {
            get { return _hasChanges; }
            private set
            {
                if (_hasChanges != value)
                {
                    _hasChanges = value;
                    OnPropertyChanged("HasChanges");
                }
            }
        }
        private Boolean _hasChanges;

        /// <summary>
        /// True if at least one call is
        /// in progress; otherwise, false
        /// </summary>
        public Boolean IsBusy
        {
            get { return _isBusy; }
            private set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    OnPropertyChanged("IsBusy");
                }
            }
        }
        private Boolean _isBusy;

        public event EventHandler<ResultsArgs<IssueType>> GetIssueTypesCompleted;
        public event EventHandler<ResultsArgs<Platform>> GetPlatformsCompleted;
        public event EventHandler<ResultsArgs<Resolution>> GetResolutionsCompleted;
        public event EventHandler<ResultsArgs<SecurityQuestion>> GetSecurityQuestionsCompleted;
        public event EventHandler<ResultsArgs<Status>> GetStatusesCompleted;
        public event EventHandler<ResultsArgs<SubStatus>> GetSubStatusesCompleted;
        public event EventHandler<ResultsArgs<User>> GetUsersCompleted;
        public event EventHandler<ResultsArgs<Issue>> GetAllIssuesCompleted;
        public event EventHandler<ResultsArgs<Issue>> GetMyIssuesCompleted;
        public event EventHandler<ResultArgs<User>> GetCurrentUserCompleted;
        public event EventHandler<ResultArgs<bool>> GetCurrentUserProfileResetCompleted;
        public event EventHandler<ResultsArgs<Issue>> GetAllUnresolvedIssuesCompleted;
        public event EventHandler<ResultsArgs<string>> GetActiveBugCountByMonthCompleted;
        public event EventHandler<ResultsArgs<string>> GetResolvedBugCountByMonthCompleted;
        public event EventHandler<ResultsArgs<string>> GetActiveBugCountByPriorityCompleted;
        public event EventHandler<ResultsArgs<IssueHistory>> GetIssueHistoryByIssueIDCompleted;
        public event EventHandler<ResultArgs<string>> SaveChangesCompleted;

        #endregion "IIssueVisionModel Interface implementation"

        #region "INotifyPropertyChanged Interface implementation"
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion "INotifyPropertyChanged Interface implementation"

        #region "Private Methods"

        /// <summary>
        /// Event handler for GetIssueTypesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetIssueTypesCompleted(object sender, GetIssueTypesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetIssueTypesCompleted != null)
                {
                    GetIssueTypesCompleted(this, new ResultsArgs<IssueType>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetIssueTypesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetIssueTypesCompleted(this, new ResultsArgs<IssueType>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetPlatformsCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetPlatformsCompleted(object sender, GetPlatformsCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetPlatformsCompleted != null)
                {
                    GetPlatformsCompleted(this, new ResultsArgs<Platform>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetPlatformsCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetPlatformsCompleted(this, new ResultsArgs<Platform>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetResolutionsCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetResolutionsCompleted(object sender, GetResolutionsCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetResolutionsCompleted != null)
                {
                    GetResolutionsCompleted(this, new ResultsArgs<Resolution>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetResolutionsCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetResolutionsCompleted(this, new ResultsArgs<Resolution>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetSecurityQuestionsCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetSecurityQuestionsCompleted(object sender, GetSecurityQuestionsCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetSecurityQuestionsCompleted != null)
                {
                    GetSecurityQuestionsCompleted(this, new ResultsArgs<SecurityQuestion>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetSecurityQuestionsCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetSecurityQuestionsCompleted(this, new ResultsArgs<SecurityQuestion>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetStatusesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetStatusesCompleted(object sender, GetStatusesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetStatusesCompleted != null)
                {
                    GetStatusesCompleted(this, new ResultsArgs<Status>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetStatusesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetStatusesCompleted(this, new ResultsArgs<Status>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetSubStatusesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetSubStatusesCompleted(object sender, GetSubStatusesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetSubStatusesCompleted != null)
                {
                    GetSubStatusesCompleted(this, new ResultsArgs<SubStatus>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetSubStatusesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetSubStatusesCompleted(this, new ResultsArgs<SubStatus>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetUsersCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetUsersCompleted(object sender, GetUsersCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetUsersCompleted != null)
                {
                    GetUsersCompleted(this, new ResultsArgs<User>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetUsersCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetUsersCompleted(this, new ResultsArgs<User>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetAllIssuesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetAllIssuesCompleted(object sender, GetAllIssuesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetAllIssuesCompleted != null)
                {
                    GetAllIssuesCompleted(this, new ResultsArgs<Issue>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetAllIssuesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetAllIssuesCompleted(this, new ResultsArgs<Issue>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetMyIssuesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetMyIssuesCompleted(object sender, GetMyIssuesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetMyIssuesCompleted != null)
                {
                    GetMyIssuesCompleted(this, new ResultsArgs<Issue>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetMyIssuesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetMyIssuesCompleted(this, new ResultsArgs<Issue>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetCurrentUserCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetCurrentUserCompleted(object sender, GetCurrentUserCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetCurrentUserCompleted != null)
                {
                    GetCurrentUserCompleted(this, new ResultArgs<User>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetCurrentUserCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetCurrentUserCompleted(this, new ResultArgs<User>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetCurrentUserProfileResetCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetCurrentUserProfileResetCompleted(object sender, GetCurrentUserProfileResetCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetCurrentUserProfileResetCompleted != null)
                {
                    GetCurrentUserProfileResetCompleted(this, new ResultArgs<bool>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetCurrentUserProfileResetCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetCurrentUserProfileResetCompleted(this, new ResultArgs<bool>(false, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetAllUnResolvedIssuesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetAllUnResolvedIssuesCompleted(object sender, GetAllUnResolvedIssuesCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetAllUnresolvedIssuesCompleted != null)
                {
                    GetAllUnresolvedIssuesCompleted(this, new ResultsArgs<Issue>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetAllUnresolvedIssuesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetAllUnresolvedIssuesCompleted(this, new ResultsArgs<Issue>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetActiveBugCountByMonthCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetActiveBugCountByMonthCompleted(object sender, GetActiveBugCountByMonthCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetActiveBugCountByMonthCompleted != null)
                {
                    GetActiveBugCountByMonthCompleted(this, new ResultsArgs<string>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetActiveBugCountByMonthCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetActiveBugCountByMonthCompleted(this, new ResultsArgs<string>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetResolvedBugCountByMonthCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetResolvedBugCountByMonthCompleted(object sender, GetResolvedBugCountByMonthCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetResolvedBugCountByMonthCompleted != null)
                {
                    GetResolvedBugCountByMonthCompleted(this, new ResultsArgs<string>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetResolvedBugCountByMonthCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetResolvedBugCountByMonthCompleted(this, new ResultsArgs<string>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetActiveBugCountByPriorityCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetActiveBugCountByPriorityCompleted(object sender, GetActiveBugCountByPriorityCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetActiveBugCountByPriorityCompleted != null)
                {
                    GetActiveBugCountByPriorityCompleted(this, new ResultsArgs<string>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetActiveBugCountByPriorityCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetActiveBugCountByPriorityCompleted(this, new ResultsArgs<string>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for GetIssueHistoryByIssueIDCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_GetIssueHistoryByIssueIDCompleted(object sender, GetIssueHistoryByIssueIDCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();

            if (e.Error == null)
            {
                if (GetIssueHistoryByIssueIDCompleted != null)
                {
                    GetIssueHistoryByIssueIDCompleted(this, new ResultsArgs<IssueHistory>(e.Result, e.Error, e.Cancelled, e.UserState));
                }
            }
            else
            {
                if (GetIssueHistoryByIssueIDCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        GetIssueHistoryByIssueIDCompleted(this, new ResultsArgs<IssueHistory>(null, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }
        }

        /// <summary>
        /// Event handler for CollectionChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Files_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (File item in e.NewItems)
                    ((INotifyPropertyChanged)(item)).PropertyChanged += IssueVisionModel_PropertyChanged;
            }
            if (e.OldItems != null)
            {
                foreach (File item in e.OldItems)
                    ((INotifyPropertyChanged)(item)).PropertyChanged -= IssueVisionModel_PropertyChanged;
            }
        }

        /// <summary>
        /// Event handler for CollectionChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Attributes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (EntityModel.Attribute item in e.NewItems)
                    ((INotifyPropertyChanged)(item)).PropertyChanged += IssueVisionModel_PropertyChanged;
            }
            if (e.OldItems != null)
            {
                foreach (EntityModel.Attribute item in e.OldItems)
                    ((INotifyPropertyChanged)(item)).PropertyChanged -= IssueVisionModel_PropertyChanged;
            }
        }

        /// <summary>
        /// Event handler for PropertyChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IssueVisionModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("HasChanges"))
            {
                ReCalculateHasChanges();
            }
        }

        /// <summary>
        /// Function to re-calculate HasChanges based on
        /// the values of _currentEditIssue and _currentEditUser
        /// </summary>
        private void ReCalculateHasChanges()
        {
            // re-calculate HasChanges for both CurrentEditIssue and CurrentEditUser
            HasChanges = CurrentEditIssueHasChanges() || CurrentEditUserHasChanges();
        }
        
        /// <summary>
        /// Function to re-calculate HasChanges of _currentEditIssue.
        /// This function checks HasChanges of the _currentEditIssue
        /// itself as well as all its Navigation properties.
        /// </summary>
        /// <returns></returns>
        private bool CurrentEditIssueHasChanges()
        {
            Boolean hasChanges = false;
            if (_currentEditIssue != null)
            {
                hasChanges = _currentEditIssue.HasChanges ||
                    (_currentEditIssue.Platform == null ? false : _currentEditIssue.Platform.HasChanges) ||
                    (_currentEditIssue.Attributes == null ? false : _currentEditIssue.Attributes.Any(n => n.HasChanges)) ||
                    (_currentEditIssue.Files == null ? false : _currentEditIssue.Files.Any(n => n.HasChanges));
            }
            return hasChanges;
        }

        /// <summary>
        /// Function to re-calculate HasChanges of _currentEditUser.
        /// </summary>
        /// <returns></returns>
        private bool CurrentEditUserHasChanges()
        {
            Boolean hasChanges = false;
            if (_currentEditUser != null)
            {
                hasChanges = _currentEditUser.HasChanges;
            }
            return hasChanges;
        }

        /// <summary>
        /// Event handler for PropertyChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("ActiveCallCount"))
            {
                // re-calculate IsBusy
                IsBusy = (_proxy.ActiveCallCount != 0);
            }
        }

        /// <summary>
        /// Event handler for UpdateIssueCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_UpdateIssueCompleted(object sender, UpdateIssueCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();
            _updateIssueDone = true;

            string warningMessage = string.Empty;
            long updatedIssueId = 0;
            if (e.Error == null)
            {
                if (e.Result.Count() == 2)
                {
                    warningMessage = e.Result[0] as string;
                    updatedIssueId = Convert.ToInt64(e.Result[1]);
                }
            }

            if (e.Error == null && string.IsNullOrEmpty(warningMessage))
            {
                // first check whether this is an update of a new issue
                if (_currentEditIssue.ChangeTracker.State == ObjectState.Added)
                {
                    // get the new issue ID returned
                    _currentEditIssue.IssueID = updatedIssueId;
                }
                // if there is no error, call AcceptChanges() first
                // we need to call AcceptChanges() on the issue itself
                // as well as all its Navigation properties
                _currentEditIssue.AcceptChanges();
                if (_currentEditIssue.Platform != null)
                {
                    _currentEditIssue.Platform.AcceptChanges();
                }
                if (_currentEditIssue.Attributes != null)
                {
                    foreach (EntityModel.Attribute item in _currentEditIssue.Attributes)
                        item.AcceptChanges();
                }
                if (_currentEditIssue.Files != null)
                {
                    foreach (File item in _currentEditIssue.Files)
                        item.AcceptChanges();
                }
            }
            else
            {
                // if there is an error, we need to send notification on first occurrence,
                // in other words, if _lastError is still null
                if (SaveChangesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        SaveChangesCompleted(this, new ResultArgs<string>(warningMessage, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }

            // we need to send notification when both _updateIssueDone
            // and _updateUserDone are true, and if there is no error.
            if (_updateIssueDone && _updateUserDone)
            {
                if (SaveChangesCompleted != null && _lastError == null && string.IsNullOrEmpty(warningMessage))
                {
                    SaveChangesCompleted(this, new ResultArgs<string>(string.Empty, e.Error, e.Cancelled, e.UserState));
                }
            }
        }

        /// <summary>
        /// Event handler for UpdateUserCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_UpdateUserCompleted(object sender, UpdateUserCompletedEventArgs e)
        {
            _proxy.DecrementCallCount();
            _updateUserDone = true;

            string warningMessage = string.Empty;
            if (e.Error == null)
            {
                warningMessage = e.Result;
            }

            if (e.Error == null && string.IsNullOrEmpty(warningMessage))
            {
                // if there is no error, call AcceptChanges() first
                _currentEditUser.AcceptChanges();
            }
            else
            {
                // if there is an error, we need to send notification on first occurrence,
                // in other words, if _lastError is still null
                if (SaveChangesCompleted != null)
                {
                    if (_lastError == null || AllowMultipleErrors)
                    {
                        SaveChangesCompleted(this, new ResultArgs<string>(warningMessage, e.Error, e.Cancelled, e.UserState));
                    }
                }
                _lastError = e.Error;
            }

            // we need to send notification when both _updateIssueDone
            // and _updateUserDone are true, and if there is no error.
            if (_updateIssueDone && _updateUserDone)
            {
                if (SaveChangesCompleted != null && _lastError == null && string.IsNullOrEmpty(warningMessage))
                {
                    SaveChangesCompleted(this, new ResultArgs<string>(string.Empty, e.Error, e.Cancelled, e.UserState));
                }
            }
        }

        #endregion "Private Methods"              
    }
}
